package client;


import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;


/**
 * ChatClient Start a connection with the server, set a unique userID for the
 * client connected. No mutable fields
 * 
 */
public class MacheClient implements Runnable {

    private final Socket client;
    private final String userID;
    private final MacheModel model;
    private PrintWriter out;
    private BufferedReader in ;
    protected Thread listener;    
    private boolean test;
    /**
     * Constructor for the ChatClient 
     * @param userIDwanted
     * @param model
     * @param localhost
     * @param port
     * @param test
     *            True only if in unit testing mode
     * @throws IOException
     * @throws BadUserNameException
     */
    public MacheClient(String userIDwanted, MacheModel model, String localhost, int port, boolean test) throws IOException,
            BadUserNameException {
        this.test = test;
        if(!test){
            client = new Socket(localhost, port);
            this.in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            this.out = new PrintWriter(new DataOutputStream(client.getOutputStream()));
            userID = setUserID(userIDwanted);
            if (userID == null) {
                client.close();
                throw new BadUserNameException();
            }
        }
        else{
            client = null;
            userID = userIDwanted;
        }
        
        this.model = model;
        listener = new Thread (this);
        listener.start();
    }

    /**
     * Always listening to the socket where the client is connected to Handle
     * any response received
     * 
     */
    @Override
    public void run() {   
        if(!test){
            try {
                handleServerResponse();         
            } catch (IOException e) {
                e.printStackTrace();
            } finally{
                try {
                    out.close();
                    client.close();  
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 
     * Handles server response line by line 
     * 
     * @throws IOException if the client socket is closed
     */
    protected void handleServerResponse() throws IOException {
        for (String line = in.readLine(); line != null; line = in.readLine()) {
            parseResponse(line, model, userID);
        }
    }

    /**
     * Send a well-formatted request to the server
     * 
     * @param request
     *            to sent
     */
    public void sendRequest(String request) {
        out.println(request);
        out.flush();
    }

    /**
     * Parse response from the server, and update model accordingly
     * 
     * @param input
     *            Response from the server
     * @param model
     *            ChatModel to make changes on
     * @param userID
     *            userID of the client
     *
     */
    protected static void parseResponse(String input, MacheModel model,String userID) {
        String[] tokens = input.split(" ");
        if (tokens.length >= 2) {
            if (tokens[0].equals("startconv")) {
                String convID = tokens[1];
                ArrayList<String> users = new ArrayList<String>();
                for (int i = 2; i < tokens.length; i++) {
                    users.add(tokens[i]);
                }
                model.addConv(convID, users);

            } else if (tokens[0].equals("activelist")) {
                List<String> usersArr = new ArrayList<String>();
                for (int i = 1; i < tokens.length; i++) {
                    usersArr.add(tokens[i]);
                }
                model.activeList(usersArr);

            } else if (tokens[0].equals("activelistwin")) {
                String convID = tokens[1];
                List<String> usersArr = new ArrayList<String>();
                for (int i = 2; i < tokens.length; i++) {
                    usersArr.add(tokens[i]);
                }
                model.activeListWin(usersArr, convID);

            } else if (tokens[0].equals("usersinconv")) {
                String convID = tokens[1];
                List<String> usersArr = new ArrayList<String>();
                for (int i = 2; i < tokens.length; i++) {
                    usersArr.add(tokens[i]);
                }
                model.usersInConv(convID, usersArr);
            } else if (tokens[0].equals("logoff")) {
                String friendID = tokens[1];
                model.friendLogOff(friendID);
            } else if (tokens[0].equals("exit")){
                String friendID = tokens[1];
                String convID = tokens[2];
                model.friendLeft(convID,friendID);                
            }else if (tokens[0].equals("text")) {
                String convID = tokens[1];
                String username = tokens[2];
                String font = tokens[3];
                String message = "";
                for (int count = 4; count < tokens.length; count++) {
                    message = message + tokens[count] + " ";
                }
                model.messageReceived(convID, username, font, message);
            }else if (tokens[0].equals("typestatus")){
                String convID = tokens[1];
                String username = tokens[2];
                String status = tokens[3];
                model.typeStatusUpdate(convID,username,status);
            }else if (tokens[0].equals("nudged")){
                String convID = tokens[1];
                String sender = tokens[2];
                String receiver = tokens[3];
                if (receiver.equals(userID)){
                    model.youGotNudged(convID,sender) ;
                }
            }else if(tokens[0].equals("newinvites")){
                String convID = tokens[1];
                List<String> usersArr = new ArrayList<String>();
                for (int i = 2; i < tokens.length; i++) {
                    usersArr.add(tokens[i]);
                }
                model.newJoinedFriends(convID,usersArr);
            }
        }
    }

    /**
     * Connects the server for the first time, check if the userID the user
     * wants already exists. If not exist return the userID, else return null
     * 
     * @return userID if not already existed, otherwise return null
     */
    private String setUserID(String userIDwanted) {

        boolean okay = false;
        try {
            out.println("-new " + userIDwanted);
            out.flush();
            String line = in.readLine();
            okay = line.equals("status okayID");
            
        } catch (IOException e) {
            System.out.println("oooops");  
        } 
        if (okay) {
            return userIDwanted;
        }
        
        return null;

    }

    /**
     * Get the userID of the ChatClient instance
     * 
     * @param userIDwanted
     * @return
     */
    public String getUserID() {
        return userID;
    }

}
